package com.k8.registry;


import com.k8.common.factroy.BeanFactory;
import com.k8.common.util.timer.HashedWheelTimer;
import com.k8.rpc.metadata.AppMetaData;
import com.k8.rpc.url.ApiSignature;
import com.k8.rpc.url.URL;
import com.k8.rpc.util.URLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.k8.common.constants.CommonConstants.DEFAULT_REGISTRY_RETRY_PERIOD;
import static com.k8.common.constants.CommonKeyConstants.EXPORTER_KEY;
import static com.k8.common.constants.CommonKeyConstants.RegistryKeyConstant.*;
import static com.k8.common.constants.CommonKeyConstants.RpcKeyConstant.API_SIGNATURE_KEY;

/**
 * dubbo 在重试的时候，将重试交给了{@link HashedWheelTimer}，但是每一个registry都维护一个时间轮不是很合适
 * 这里做修改，registry不管是注册成功还是失败重试是同步的，本身是否异步调用取决于调用者的执行方式是否是异步
 * 当注册成功即发布事件
 */
public abstract class FailBackRegistry extends AbstractRegistry {
    Logger logger = LoggerFactory.getLogger(FailBackRegistry.class);
    private final long retryPeriod; //重试的时间间隔，默认5000ms

    /*private final HashedWheelTimer retryTimer;
    //定时轮的刻度选择128，重试次数不多的话足够用。重试次数太多的话刻度少容易造成时间偏差过大
    retryTimer = new HashedWheelTimer(
            DAEMON_THREAD_FACTORY, retryPeriod, TimeUnit.MILLISECONDS, 128);*/
    public FailBackRegistry(URL url, BeanFactory beanFactory) {
        super(url, beanFactory);
        this.retryPeriod = url.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD);
    }

    private static String cache_configure_key = "cacheConfigure";

    //只支持应用级注册，所以和dubbo不同，直接拆开了    todo 实际注册还没有注册
    @Override
    public void registerInstance(Object metaData, URL registerUrl) {
        if (metaData instanceof AppMetaData appMetaData) {
            metaData = resolveMetadata(appMetaData);
        }
        try {
            doRegisterInstance(metaData, registerUrl);
        } catch (RegistryException e) {
            boolean check = registerUrl.getParameter(REGISTRY_CHECK_KEY, true);
            if (check) {
                logger.warn("Register error, url: " + URLUtils.buildURLSimpleString(registerUrl));
                try {
                    Thread.sleep(retryPeriod);
                    registerInstance(metaData, registerUrl);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            } else {
                throw new RegistryException(e.getMessage());
            }
        }
    }

    protected abstract Object resolveMetadata(AppMetaData metaData);

    protected abstract AppMetaData restoreMetadata(Object metadata);

    protected abstract void doRegisterMappingAndConfiguration(URL registryConfigUrl, String configure, ApiSignature apiSignature);

    @Override
    public void registerMappingAndConfiguration(URL url) {
        String configure = null;
        try {
            URL protocolUrl = (URL) url.getAttribute(EXPORTER_KEY);
            ApiSignature apiSignature = (ApiSignature) url.getAttribute(API_SIGNATURE_KEY);
            //todo 解析合适的配置
            configure = getConfigure(protocolUrl);
            doRegisterMappingAndConfiguration(url, configure, apiSignature);
            super.registerMappingAndConfiguration(url);
        } catch (RegistryException e) {
            boolean check = url.getParameter(REGISTRY_CHECK_KEY, true);
            if (check) {
                if (url.getAttribute(cache_configure_key) == null) {
                    url.putAttribute(cache_configure_key, configure);
                }
                logger.warn("Register error, url: " + URLUtils.buildURLSimpleString(url));
                try {
                    Thread.sleep(retryPeriod);
                    registerMappingAndConfiguration(url);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            } else {
                throw new RegistryException(e.getMessage());
            }
        }
        if (url.getAttribute(cache_configure_key) != null) url.removeAttribute(cache_configure_key);
    }

    private String getConfigure(URL protocolUrl) {
        Object cache = protocolUrl.getAttribute(cache_configure_key);
        if (cache != null) return (String) cache;
        return doGetConfigure(protocolUrl);
    }

    abstract protected String doGetConfigure(URL protocolUrl);


    protected abstract void doRegisterInstance(Object appMetaData, URL registerUrl) throws RegistryException;
    /**
     * 单独罗出来，内置ProtocolUrl，借此注册协议配置
     *
     * */


}
